<?php
/**
 * 授权访问规则处理
 *
 * @author Mo yi <root@imoi.cn>
 * @link   http://www.imoi.cn
 * @copyright Copyright &copy; 2010-2012 DuoLam Software LLC
 * @license http://www.imoi.cn/license/
 * @version $Id: dpAuthRule.php 1 2012-04-22 14:41:54Z Mo.yi $
 * @package db
 * @since 1.0
 */

class dpAuthRule extends dpComponent
{
	private $_error = array(
		'0' => '请登陆后再操作',
		'1' => '您没有访问该模块的权限',
		'2' => '您没有访问该控制器的权限',
		'3' => '您没有访问该页面的权限',
		'4' => '当前禁止用户访问该模块',
		'5' => '当前禁止用户访问该控制器',
		'6' => '当前禁止用户访问该页面',
		'7' => '您的帐户还未激活',
		'8' => '当前页面不允许您所属的角色访问',
		'9' => '系统信息获取失败，暂时无法访问',
	);

	private $_control;
	private $_rules;
	private $_user;
	private $_auth;
	private $_filter;

	/**
	 * 构造方法
	 * @param array $rules 访问规则
	 * @param object $control 控制器
	 */
	public function __construct()
	{
		$this->_user  = D::app()->user()->run();
		$this->_auth  = D::app()->auth();
		
	}

	/**
	 * 运行解析
	 */
	public function run(array $rules, dpAccess &$control)
	{
		$this->_rules = $rules;
		$this->_control = &$control;
		$this->_filter = D::app()->filter($this->_user, $control);
		$this->_parseRules();
	}

	/**
	 * 解析权限检查规则
	 */
	private function _parseRules()
	{
		foreach ($this->_rules as $key => $rule) {
			if (!$this->_user->isGuest() && empty($rule['break'])) {
				if ($this->_user->isSuperAdmin($this->_user->getId()))
					return true;
				if (!$this->_user->isActivation($this->_user->getId()))
					$this->_error($this->_error[7]);
			}
			$user  = isset($rule['user']) ? $rule['user'] : '@';
			$limit = isset($rule['limit']) ? $rule['limit'] : 'user';
			$rule['control'] = isset($rule['control']) ? $rule['control'] : array('*');
			$rule['actions'] = isset($rule['actions']) ? $rule['actions'] : array('*');
			$rule['roles']   = isset($rule['roles'])   ? $rule['roles']   : array('*');
			unset($rule['user'], $rule['limit']);

			if ($rule['access'] == 'allow') {
				if ($this->_accessAllow($rule, $user, $limit)) return true;
			} else {
				$this->_accessDeny($rule, $user, $limit);
			}
		}
	}

	/**
	 * 拒绝访问的授权检查，登陆检查只针对动作。
	 * @param $rule array  权限检验规则
	 * @param $user string 检查类型 ? @ * (未登录，已登陆，所有)
	 * @param $limit string 限制检查域 user group whole (用户，所有，联合)
	 */
	private function _accessDeny($rule, $user, $limit)
	{
		$control = $this->_control->getControl();
		$module  = $this->_control->getModule();
		$action  = $this->_control->getAction();

		if ($module && isset($rule['module'])) {
			if($module == $rule['module']) $this->_error($this->_error[4]);
		}

		if ($control && $module) {

			if ($rule['control'][0] == '*' || in_array($control, $rule['control'])) $status = true;
			else $status = false;

			if ($status) $this->_error($this->_error[5]);
		}

		if ($action) {

			if ($user == '@' && $this->_user->isGuest()) {
				if (!empty($this->_user->loginUrl)) $this->_login($user);
				else $this->_error($this->_error[0]);
			}

			if ($rule['actions'][0] == '*' || in_array($action, $rule['actions']))
				$this->_error($this->_error[6]);
			else if ($rule['roles'] != '*') {
				$roles   = $this->_getRole($limit);
				foreach ($roles as $key => $val)
					in_array($val['role_name'], $rule['roles']) && $this->_error($this->_error[8]);
			}

			if (isset($rule['filter']) && !$this->_filter->run($rule['filter']))
				$this->_error($this->_error[8]);
		}
	}

	/**
	 * 允许访问的授权检查，登陆检查只针对动作。
	 * @param $rule array  权限检验规则
	 * @param $user string 检查类型 ? @ * (未登录，已登陆，所有)
	 * @param $limit string 限制检查域 user group whole (用户，所有，联合)
	 */
	private function _accessAllow($rule, $user, $limit)
	{
		$control = $this->_control->getControl();
		$module  = $this->_control->getModule();
		$action  = $this->_control->getAction();
		$behavior = $this->_getBehavior($limit);

		if ($module && $user == '@') {
			if ($this->_user->isGuest() && $user == '@') {
				if (!empty($this->_user->loginUrl)) $this->_login($user);
				else $this->_error($this->_error[0]);
			} else if($user == '@'){
				$modules = $this->_getListStr($behavior, 'access_module');
				if (!in_array($module, $modules)) $this->_error($this->_error[1]);
			}
		}

		if ($control && $module && $user == '@') {
			foreach ($behavior as $beha) {
				if ($module == $beha['access_module']) {
					$controls = explode('|', $beha['access_control']);
					break;
				}
			}

			if ($rule['control'][0] != '*' && $controls[0] != '*' && !in_array($control, $rule['control']))
				$this->_error($this->_error[5]);

			if ($rule['control'][0] != '*' && $controls[0] != '*' && !in_array($control, $controls))
				$this->_error($this->_error[2]);
				
		}
		
		if ($action) {

			if (in_array($action, $rule['actions']) || $rule['actions'][0] == '*') $status = true;
			else $status = false;

			if ($user == '@' && $this->_user->isGuest()) {
				if (!empty($this->_user->loginUrl)) $this->_login($user);
				else $this->_error($this->_error[0]);
			} else {
				if ($user == '@' && $status) {
					$actions = $this->_getListStr($behavior);
					$roles   = $this->_getRole($limit);

					if ($rule['roles'][0] != '*') {
						foreach ($roles as $key => $val) {
							if (in_array($val['role_name'], $rule['roles'])) break;
							else $this->_error($this->_error[8]);
						}
					} else if($rule['actions'][0] != '*' && !in_array($action, $actions))
						$this->_error($this->_error[3]);

					if (isset($rule['filter']) && !$this->_filter->run($rule['filter']))
						$this->_error($this->_filter->getError());
					else
						return true;
				} else if($status) {
					if (isset($rule['filter']) && !$this->_filter->run($rule['filter']))
						$this->_error($this->_filter->getError());
					else
						return true;
				}
			}
		}

	}

	/**
	 * 检查用户状态，是否是游客
	 *
	 */
	private function _checkUserState($user)
	{
		if ($this->_user->isGuest() && $this->_checkGuest($user)) return true;
		else return false;
	}

	/**
	 * 执行登陆页面跳转
	 */
	private function _login($user)
	{
		if ($this->_user->isGuest() && !$this->_checkGuest($user)) {
			header('Location:'.$this->_user->loginUrl);
			exit;
		}
	}

	
	/**
	 * 错误信息显示
	 *
	 */
	private function _error($message)
	{
		header('WWW-Autherticate: Basic realm="Member Area"');
		header("HTTP/1.0 401 Unauthorized");
		$auth = $this->setConfig('access','auth');

		if (empty($auth['auth_file'])) {
			D::error($message);
		} else {
			if (is_file($auth['auth_file']) && !empty($auth['var'])) {
				extract($auth['var']); require($auth['auth_file']);
			} else {
				dpException::error('权限提示页面不存在或者未设置信息变量名');
			}
		}
	}

	/**
	 * 检查是否允许游客
	 * @return string $user 允许类型
	 * @return boolean
	 */
	private function _checkGuest($user)
	{
		if ($user == '?' || $user == '*')
			return true;
		else
			return false;
	}

	/**
	 * 获取角色
	 */
	private function _getRole($limit)
	{
		if ($limit == 'whole') {
			$userRole = $this->_auth->getUserRole($this->_user->getId());
			$groupRole = $this->_auth->getGroupRole($this->_user->getGid());

			if (count($userRole) > count($groupRole))
				$this->_unique($userRole, $groupRole, 'rid');
			else
				$this->_unique($groupRole, $userRole, 'rid');

			if(empty($userRole) && empty($groupRole))
				return array();
			else if(empty($userRole))
				return array_merge($groupRole,array());
			else 
				return array_merge($userRole, array());

		} else if($limit == 'user') {
			return $this->_auth->getUserRole($this->_user->getId());
		} else {
			return $this->_auth->getGroupRole($this->_user->getGid());
		}
	}

	/**
	 * 获取行为
	 * @param string $limit 限制域，分为:user group whole
	 * user 是基于用户的角色
	 * group 是基于组的角色
	 * whole 是不限制，两者融合
	 * @return array
	 */
	private function _getBehavior($limit)
	{
		if ($limit == 'whole') {
			$userBehavior  = $this->_auth->getUserBehavior($this->_user->getId());
			$groupBehavior = $this->_auth->getGroupBehavior($this->_user->getGid());

			if (count($userBehavior) > count($groupBehavior))
				$this->_unique($userBehavior, $groupBehavior);
			else
				$this->_unique($groupBehavior, $userBehavior);

			if(empty($userBehavior) && empty($groupBehavior))
				return array();
			else if(empty($userBehavior))
				return array_merge($groupBehavior,array());
			else return
				array_merge($userBehavior, array());

		} else if($limit == 'user') {
			return $this->_auth->getUserBehavior($this->_user->getId());
		} else {
			return $this->_auth->getGroupBehavior($this->_user->getGid());
		}

	}

	/**
	 * 去除重复的值
	 * @param array $data 数据1
	 * @param array $data2 数据2
	 * @param string $name 键名
	 */
	private function _unique(&$data, &$data2, $name = 'bid')
	{
		if (empty($data) || empty($data2)) return array();
		foreach ($data as $k => $v) foreach ($data2 as $k1 => $v1) if ($v[$name] == $v1[$name]) unset($data2[$k1]);
	}

	/**
	 * 外部获取格式化后的行为
	 */
	public function getBehavior($limit = 'whole')
	{
		return $this->_getListStr($this->_getBehavior($limit));
	}

	/**
	 * 获取格式化后的行为
	 * @param array $behavior 行为列表
	 * @param string $type    行为类型
	 * @return array
	 */
	private function _getListStr($behavior, $type = 'access_action')
	{
		if (empty($behavior)) return array();

		$str = '';

		foreach ($behavior as $val) $str.= $val[$type] .'|';
		
		$str = trim($str,'|');
		return explode('|', $str);
	}

}
?>